﻿#region

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using KLadderProposal.Interfaces;
using KLadderProposal.Interfaces.BasicStructures;

#endregion

namespace KLadderProposal.Algorithms
{
    public class StructureAlgorithm : IAlgorithm
    {
        #region Fields (6)

        private int _cliqueSize = 5;
        private int _clusterNumber = 1;
        private int _counterTick;
        private string _path = string.Format(@"c:\temp\Results-{0:d-h-m-s}-continues.txt", DateTime.Now);
        private string _path1 = string.Format(@"c:\temp\Results-{0:d-h-m-s}.txt", DateTime.Now);
        private int _treshold = 4;

        #endregion Fields

        #region Properties (4)

        public int CliqueSize
        {
            get { return _cliqueSize; }
            set { _cliqueSize = value; }
        }

        public string Text { get; set; }

        public int Threshold
        {
            get { return _treshold; }
            set { _treshold = value; }
        }

        public double Treshold
        {
            get { return _treshold; }
            set { _treshold = (int)value; }
        }

        #endregion Properties

        #region Methods (20)

        // Public Methods (7) 

        public void Run(IGraph graph)
        {
            _path = string.Format(@"c:\temp\{0}{1}-{2}-{3}-continues.txt",
                                  "", //DateTime.Now,
                                  Text.Replace(".txt", ""),
                                  CliqueSize,
                                  Treshold);
            _path1 = string.Format(@"c:\temp\{0}{1}-{2}-{3}.txt",
                                   "", //DateTime.Now,
                                   Text.Replace(".txt", ""),
                                   CliqueSize,
                                   Treshold);


            using (StreamWriter sw = File.AppendText(_path))
            {
                sw.WriteLine("DateTime {0} ,Start Graph {1} ,CliqueSize {2} ,Threshold {3}",
                             DateTime.Now,
                             Text,
                             CliqueSize,
                             Threshold);
            }
            using (StreamWriter sw = File.AppendText(_path1))
            {
                sw.WriteLine("DateTime {0} ,Start Graph {1} ,CliqueSize {2} ,Threshold {3}",
                             DateTime.Now,
                             Text,
                             CliqueSize,
                             Threshold);
            }
            graph.SortGraphByNeighbors();

            while (true)
            {
                InitGraph(graph);
                List<GraphNode> nodes;
                if (!FindCliqueInGraph(graph, out nodes)) return;

                using (StreamWriter sw = File.AppendText(_path1))
                {
                    sw.WriteLine("DateTime {0} Find Clique {1}", DateTime.Now, _clusterNumber);
                }
                using (StreamWriter sw = File.AppendText(_path))
                {
                    sw.WriteLine("DateTime {0} Find Clique {1}", DateTime.Now, _clusterNumber);
                }
                ExpandCluster(nodes);
                WriteClusterToFile(nodes);
                _clusterNumber++;
            }
        }

        public bool IsNeighborsInCluster(List<GraphNode> commonElements)
        {
            foreach (GraphNode a in commonElements)
            {
                if (a.IsMarkedAsDeleted)
                    continue;
                foreach (GraphNode b in commonElements)
                {
                    if (b.IsMarkedAsDeleted ||
                        a.Label == b.Label)
                        continue;
                    foreach (GraphNode c in commonElements)
                    {
                        if (c.IsMarkedAsDeleted ||
                            a.Label == c.Label ||
                            b.Label == c.Label) continue;
                        foreach (GraphNode d in commonElements)
                        {
                            if (d.IsMarkedAsDeleted ||
                                a.Label == d.Label ||
                                b.Label == d.Label ||
                                c.Label == d.Label) continue;
                            List<GraphNode> e = new List<GraphNode>(new[] { a, b, c, d });
                            if (IsNClique(e))
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        }

        // Private Methods (13) 

        private void AddNodeToBiggerCluster(int size, SortedDictionary<string, GraphNode> cliqueNodes, GraphNode item,
                                            SortedDictionary<string, SortedDictionary<string, GraphNode>> list2)
        {
            SortedDictionary<string, GraphNode> dict = new SortedDictionary<string, GraphNode>();
            int[] stamp = new int[size];
            int index = 0;

            foreach (KeyValuePair<string, GraphNode> item2 in cliqueNodes)
            {
                stamp[index] = item2.Value.SequenceNumber;
                dict.Add(item2.Key, item2.Value);
                index++;
                //item2.Value.IsMarkedAsDeleted = true;
                //item2.Value.ClusterNumber = clusterNumber;
            }

            stamp[index] = item.SequenceNumber;
            dict.Add(item.Label, item);
            Array.Sort(stamp);
            string key = string.Join(",", stamp);

            if (list2.ContainsKey(key)) //make sure that  duplicate clique not added
            {
                if (FindCommonItems(list2[key], dict).Count > size - 1)
                    return;
            }
            list2.Add(key, dict);
        }

        private bool Containes(IList<GraphNode> outer, List<GraphNode> inner)
        {
            int i = 0;
            List<GraphNode> nodes = new List<GraphNode>();

            foreach (GraphNode outNode in outer)
            {
                if (outNode.IsMarkedAsDeleted) continue;
                foreach (GraphNode innerNode in inner)
                {
                    if (outNode.Label == innerNode.Label)
                    {
                        nodes.Add(outNode);
                        i++;
                    }
                    if (i != Threshold) continue;
                    if (IsNClique(nodes))
                        return true;
                    i = 0;
                    nodes.Clear();
                }
            }
            return false;
        }

        private void ExpandCluster(List<GraphNode> nodes)
        {
            while (true)
            {
                GraphNode node = ExpandToCluster(nodes);
                if (node != null)
                    nodes.Add(node);
                else
                    break;
            }
        }

        private GraphNode ExpandToCluster(List<GraphNode> nodes)
        {
            foreach (GraphNode a in nodes)
            {
                if (a.IsMarkedAsDeleted)
                    continue;
                foreach (GraphNode b in a.Neighbors.Values)
                {
                    if (b.IsMarkedAsDeleted)
                        continue;
                    _counterTick++;
                    if (_counterTick > 100000)
                    {
                        _counterTick = 0;
                        using (StreamWriter sw = File.AppendText(_path))
                        {
                            sw.WriteLine("DateTime {0} ExpandToCluster CounterTick {1} node In cluster {2}",
                                         DateTime.Now,
                                         _clusterNumber,
                                         nodes.Count);
                        }
                    }

                    if (!Containes(b.Neighbors.Values, nodes)) continue;
                    if (nodes.Contains(b)) continue;
                    return b;
                }
            }
            return null;
        }

        private IEnumerable<SortedDictionary<string, GraphNode>> Find3Clique(IEnumerable<GraphNode> graph)
        {
            List<SortedDictionary<string, GraphNode>> lists = new List<SortedDictionary<string, GraphNode>>();
            int cliqueSequnceNumber = 0;

            foreach (GraphNode root in graph)
            {
                if (root.IsMarkedAsDeleted ||
                    root.ThreeCliqueNumber != 0)
                    continue;
                foreach (KeyValuePair<string, GraphNode> neighbor in root.Neighbors)
                {
                    if (neighbor.Value.IsMarkedAsDeleted ||
                        neighbor.Value.ThreeCliqueNumber != 0)
                        continue;
                    SortedList<string, GraphNode> list = FindCommonItems(root.Neighbors, neighbor.Value.Neighbors);
                    foreach (KeyValuePair<string, GraphNode> item in list)
                    {
                        if (item.Value.ThreeCliqueNumber != 0)
                            continue;
                        cliqueSequnceNumber++;

                        SortedDictionary<string, GraphNode> dic = new SortedDictionary<string, GraphNode>();
                        if (root.Label == neighbor.Key ||
                            root.Label == item.Key ||
                            neighbor.Key == item.Key) continue;

                        cliqueSequnceNumber++;
                        dic.Add(root.Label, root);
                        dic.Add(neighbor.Key, neighbor.Value);
                        dic.Add(item.Key, item.Value);

                        root.ThreeCliqueNumber = cliqueSequnceNumber;
                        neighbor.Value.ThreeCliqueNumber = cliqueSequnceNumber;
                        item.Value.ThreeCliqueNumber = cliqueSequnceNumber;

                        lists.Add(dic);
                    }
                }
            }
            return lists;
        }

        private SortedDictionary<string, SortedDictionary<string, GraphNode>>
            FindBiggerClique(IGraph graph, IEnumerable<SortedDictionary<string, GraphNode>> clique, int size)
        {
            SortedDictionary<string, SortedDictionary<string, GraphNode>> list2 =
                new SortedDictionary<string, SortedDictionary<string, GraphNode>>();
            //  int clusterNumber = 0;
            foreach (SortedDictionary<string, GraphNode> cliqueNodes in clique)
            {
                foreach (GraphNode item in graph.Nodes)
                {
                    if (item.IsMarkedAsDeleted)
                        continue;
                    if (cliqueNodes.ContainsKey(item.Label))
                        continue;

                    List<KeyValuePair<string, GraphNode>> commonElements =
                        item.Neighbors.Intersect(cliqueNodes).ToList();
                    int count = 0;
                    foreach (KeyValuePair<string, GraphNode> item1 in commonElements)
                    {
                        if (!item1.Value.IsMarkedAsDeleted)
                            count++;
                    }
                    if (count < size - 1)
                        continue;
                    //    if (!Contains(item.Neighbors, cliqueNodes, size - 1)) continue;

                    AddNodeToBiggerCluster(size, cliqueNodes, item, list2);
                    break;
                }
            }
            return list2;
        }

        private bool FindCliqueInGraph(IGraph graph, out List<GraphNode> nodes)
        {
            if (FindCliqueInGraph(graph, out nodes, CliqueSize))
                return true;
            nodes = null;
            return false;
        }

        private bool FindCliqueInGraph(IGraph graph, out List<GraphNode> nodes, int cliqueSize)
        {
            nodes = new List<GraphNode>();
            IEnumerable<SortedDictionary<string, GraphNode>> clique3List = Find3Clique(graph.Nodes);
            SortedDictionary<string, SortedDictionary<string, GraphNode>> clique4List;

            clique4List = FindBiggerClique(graph, clique3List, 4);
            for (int i = 5; i < cliqueSize + 1; i++)
            {
                clique4List = FindBiggerClique(graph, clique4List.Values, i);
            }

            foreach (KeyValuePair<string, SortedDictionary<string, GraphNode>> keyValuePair in clique4List)
            {
                foreach (KeyValuePair<string, GraphNode> node in keyValuePair.Value)
                {
                    nodes.Add(node.Value);
                }
                return true;
            }
            nodes = null;
            return false;
        }

        private Dictionary<string, GraphNode> FindCommonItems(SortedDictionary<string, GraphNode> sortedList1,
                                                              IDictionary<string, GraphNode> sortedList2)
        {
            Dictionary<string, GraphNode> nodes = new Dictionary<string, GraphNode>();
            foreach (KeyValuePair<string, GraphNode> item in sortedList1)
            {
                if (sortedList2.ContainsKey(item.Key))
                {
                    nodes.Add(item.Key, item.Value);
                }
            }
            return nodes;
        }

        private SortedList<string, GraphNode> FindCommonItems(SortedList<string, GraphNode> sortedList1,
                                                              SortedList<string, GraphNode> sortedList2)
        {
            SortedList<string, GraphNode> nodes = new SortedList<string, GraphNode>();
            foreach (KeyValuePair<string, GraphNode> item in sortedList1)
            {
                if (sortedList2.ContainsKey(item.Key)) //&& item.Value.ThreeCliqueNumber != 0)
                {
                    nodes.Add(item.Key, item.Value);
                }
            }
            return nodes;
        }

        private void InitGraph(IGraph graph)
        {
            foreach (GraphNode node in graph.Nodes)
            {
                node.ThreeCliqueNumber = 0;
            }
        }

        private static bool IsNClique(List<GraphNode> nodes)
        {
            nodes.Sort((x, y) => String.Compare(x.Label, y.Label, StringComparison.Ordinal));

            for (int i = 0; i < nodes.Count - 1; i++)
            {
                bool result = false;
                List<bool> found = new List<bool>();
                found.AddRange(Enumerable.Repeat(false, nodes.Count - i - 1));
                int j = 0;

                foreach (GraphNode nodeNeighbor in nodes[i].Neighbors.Values)
                {
                    if (!found[j] && nodes[i + j + 1] == nodeNeighbor)
                    {
                        found[j] = true;
                        j++;
                    }

                    if (!found.All(foundInner => foundInner)) continue;
                    result = true;
                    break;
                }

                if (!result)
                    return false;
            }

            return true;
        }

        private void WriteClusterToFile(List<GraphNode> nodes)
        {
            using (StreamWriter sw = File.AppendText(_path1))
            {
                sw.WriteLine("DateTime {0} Find Cluster {1} cluster size {2}", DateTime.Now, _clusterNumber,
                             nodes.Count);
                sw.WriteLine();

                foreach (GraphNode graphNode in nodes)
                {
                    sw.Write(string.Format("{0}{1}", graphNode.Label, ","));
                    graphNode.IsMarkedAsDeleted = true;
                    graphNode.ClusterNumber = _clusterNumber;
                }
                sw.WriteLine();
                sw.WriteLine();
            }
        }

        #endregion Methods

        #region Parse file

        public static void ParseFile()
        {
            try
            {
                FolderBrowserDialog fd = new FolderBrowserDialog
                                             {
                                                 SelectedPath = @"C:\Users\Administrator\Documents\Results"
                                             };
                fd.ShowDialog();
                string[] oDirectories = Directory.GetFiles(fd.SelectedPath, "*.txt", SearchOption.AllDirectories);

                foreach (string oDirectory in oDirectories)
                {
                    try
                    {
                        ParseFile(oDirectory);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private static void ParseFile(string p)
        {
            string allFile = File.ReadAllText(p);

            string[] splitedFile = allFile.Split(' ');
            List<string> splitedItems = new List<string>();
            for (int i = 0; i < splitedFile.Length; i++)
            {
                if (splitedFile[i] == "cluster" && splitedFile[i + 1] == "size")
                {
                    splitedItems.Add(splitedFile[i + 2]);
                }
            }

            int count = splitedItems.Count;

            List<string> firstItem = new List<string>();
            List<int> secoundItem = new List<int>();
            int value;
            foreach (string item in splitedItems)
            {
                string[] temp = item.Split(Environment.NewLine.ToCharArray());
                {
                    int.TryParse(temp[0], out value);
                    string[] sss = temp[4].Split(',');
                    Array.Sort(sss);
                    firstItem.Add(string.Join(",", sss));
                    secoundItem.Add(value);
                }
            }
            string s5;
            int t1;
            for (int i = 0; i < secoundItem.Count; i++)
            {
                for (int j = 0; j < secoundItem.Count; j++)
                {
                    if (secoundItem[i] <= secoundItem[j]) continue;
                    t1 = secoundItem[j];
                    secoundItem[j] = secoundItem[i];
                    secoundItem[i] = t1;

                    s5 = firstItem[j];
                    firstItem[j] = firstItem[i];
                    firstItem[i] = s5;
                }
            }


            List<string> results = new List<string>();

            for (int i = 0; i < secoundItem.Count; i++)
            {
                results.Add(secoundItem[i] + "," + firstItem[i]);
            }


            //need to sort strings
            string dir = Path.GetDirectoryName(p);
            string fileName = Path.GetFileNameWithoutExtension(p);
            if (dir == null) return;
            string filePath = Path.Combine(dir, fileName + "-" + count + "new.csv");
            File.WriteAllLines(filePath, results);
        }

        private static void CompareFiles(string filePath1, string filePath2)
        {
            StringBuilder sb = new StringBuilder();
            string filePathrReader1 = File.ReadAllText(filePath1);
            string filePathrReader2 = File.ReadAllText(filePath2);
            string[] allLines1 = filePathrReader1.Split(Environment.NewLine.ToCharArray());
            string[] allLines2 = filePathrReader2.Split(Environment.NewLine.ToCharArray());
            //=new string[1]; allLines2[0] = ",," +
            //  "A3KFJ3, B0LPE8, B0LPF3, B2RZG3, B5BU57, D3DNE3, D3DWA0, E1P5V4, O60674, P00519-2, P00533, P01133, P05231, P06239, P06241, P0961, P12931, P27986, P29350, P29353, P29597, P40763, P42336, P43405, P62993, P62993-2, Q05397, Q06124";
            int length1 = allLines1.Length;
            int length2 = allLines2.Length;
            string[] splitedValues1;
            string[] splitedValues2;
            sb.AppendLine("Cluster number1,Cluster Count 1,Cluster number2,Cluster count 2,Common Nodes,Nodes");
            List<List<string>> common = new List<List<string>>();
            for (int i = 0; i < length1; i++)
            {
                for (int j = 0; j < length2; j++)
                {
                    splitedValues1 = allLines1[i].Split(',');
                    if (splitedValues1.Length < 2) continue;
                    splitedValues1 = splitedValues1.SubArray(2, splitedValues1.Length - 2);
                    splitedValues2 = allLines2[j].Split(',');
                    if (splitedValues2.Length < 2) continue;
                    splitedValues2 = splitedValues2.SubArray(2, splitedValues2.Length - 2);
                    IEnumerable<string> commonList = splitedValues1.Intersect(splitedValues2);
                    IEnumerable<string> enumerable = commonList as string[] ?? commonList.ToArray();
                    int len1 = enumerable.Count();

                    if (enumerable.Count() <= 1) continue;
                    common.Add(new List<string>(enumerable.ToArray()));
                    sb.AppendLine(string.Format("{0},{1},{2},{3},{4},{5}",
                                                i,
                                                splitedValues1.Length,
                                                j,
                                                splitedValues2.Length,
                                                len1,
                                                string.Join(",", enumerable.ToArray())));
                }
            }
            string dir = Path.GetDirectoryName(filePath1);
            string p1 = Path.GetFileNameWithoutExtension(filePath1);
            if (p1 == null) return;
            string fileName1 = p1.Substring(0, p1.Length - 6);
            string p2 = Path.GetFileNameWithoutExtension(filePath2);
            if (p2 == null) return;
            string fileName2 = p2.Substring(0, p2.Length - 6);
            if (dir == null) return;
            string filePath = Path.Combine(dir, "CommonItems-" + fileName1 + "--" + fileName2 + ".csv");
            File.WriteAllText(filePath, sb.ToString());
        }

        public static void CompareFiles()
        {
            try
            {
                string filePath1;
                string filePath2;
                using (OpenFileDialog fd = new OpenFileDialog())
                {
                    fd.InitialDirectory = @"C:\Users\Administrator\Documents\Results";
                    fd.Title = "Select first file to compare";
                    if (fd.ShowDialog() != DialogResult.OK)
                        return;
                    filePath1 = fd.FileName;
                }
                using (OpenFileDialog fd = new OpenFileDialog())
                {
                    fd.InitialDirectory = Path.GetDirectoryName(filePath1);
                    fd.Title = "Select secound file to compare";
                    if (fd.ShowDialog() != DialogResult.OK)
                        return;
                    filePath2 = fd.FileName;
                }
                CompareFiles(filePath1, filePath2);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        #endregion parse file
    }
}